package hawk.example.bookstore.sql.provider;

import static glz.hawkframework.mybatis.writer.MybatisBuilderContextImpl.*;
import static glz.hawkframework.sql.support.impl.DSL.*;
import static glz.hawkframework.support.ArgumentSupport.*;
import static glz.hawkframework.support.LogicSupport.*;
import static hawk.example.bookstore.support.BookInventorySupport.*;

import glz.hawkframework.helper.ObjectHelper;
import glz.hawkframework.mybatis.sql.MybatisParam;
import glz.hawkframework.mybatis.sql.provider.AbstractSqlProvider;
import glz.hawkframework.mybatis.statement.GeneralStatementProvider;
import glz.hawkframework.mybatis.statement.InsertStatementProvider;
import glz.hawkframework.mybatis.statement.MultiRowsInsertStatementProvider;
import glz.hawkframework.mybatis.statement.UpdateStatementProvider;
import glz.hawkframework.mybatis.statement.impl.GeneralStatementProviderImpl;
import glz.hawkframework.mybatis.statement.impl.InsertStatementProviderImpl;
import glz.hawkframework.mybatis.statement.impl.MultiRowsInsertStatementProviderImpl;
import glz.hawkframework.mybatis.statement.impl.UpdateStatementProviderImpl;
import glz.hawkframework.mybatis.writer.MybatisBuilderContext;
import glz.hawkframework.sql.condition.Condition;
import glz.hawkframework.sql.dsl.delete.Delete;
import glz.hawkframework.sql.dsl.insert.Insert;
import glz.hawkframework.sql.dsl.select.Select;
import glz.hawkframework.sql.dsl.update.Update;
import glz.hawkframework.sql.support.SqlBuilder;
import glz.hawkframework.sql.support.impl.DefaultAliasedNamedColumn;
import glz.hawkframework.sql.util.QueryWrapper;

import hawk.example.bookstore.po.BookInventoryPo;
import hawk.example.bookstore.update.BookInventoryColumnUpdate;
import hawk.example.bookstore.update.BookInventoryUpdate;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.ibatis.builder.annotation.ProviderMethodResolver;
import org.springframework.stereotype.Component;

@Component
public class BookInventorySqlProvider extends AbstractSqlProvider implements ProviderMethodResolver {
    public BookInventorySqlProvider(SqlBuilder<MybatisBuilderContext> sqlBuilder) {
        super(sqlBuilder);
    }

    protected Insert buildInsert() {
        return insertInto(BOOK_INVENTORY, COLUMNS).values(Arrays.stream(PARAM_COLUMNS).map(p->p.prefix(InsertStatementProvider.PARAM_PREFIX)).toArray()).build();
    }

    public InsertStatementProvider<BookInventoryPo> insert(BookInventoryPo bookInventoryPo) {
        String key = buildKey("insert", BookInventoryPo.class);
        String insertSql = sqlCache.computeIfAbsent(key, k -> sqlBuilder.build(buildInsert(), DUMMY_INSTANCE));
        return InsertStatementProviderImpl.builder(insertSql, bookInventoryPo).build();
    }

    protected Insert buildInsertSelective(BookInventoryPo bookInventoryPo) {
        List<DefaultAliasedNamedColumn> columns = new ArrayList<>();
        List<MybatisParam> params = new ArrayList<>();
        if (bookInventoryPo.getBookInventoryId() != null) {columns.add(BOOK_INVENTORY_ID); params.add(PARAM_BOOK_INVENTORY_ID.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getBookId() != null) {columns.add(BOOK_ID); params.add(PARAM_BOOK_ID.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getBookQuantity() != null) {columns.add(BOOK_QUANTITY); params.add(PARAM_BOOK_QUANTITY.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getLocationCode() != null) {columns.add(LOCATION_CODE); params.add(PARAM_LOCATION_CODE.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getCreateDateTime() != null) {columns.add(CREATE_DATE_TIME); params.add(PARAM_CREATE_DATE_TIME.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getUpdateDateTime() != null) {columns.add(UPDATE_DATE_TIME); params.add(PARAM_UPDATE_DATE_TIME.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getDeleteDateTime() != null) {columns.add(DELETE_DATE_TIME); params.add(PARAM_DELETE_DATE_TIME.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getCreateUserId() != null) {columns.add(CREATE_USER_ID); params.add(PARAM_CREATE_USER_ID.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getUpdateUserId() != null) {columns.add(UPDATE_USER_ID); params.add(PARAM_UPDATE_USER_ID.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getDeleteUserId() != null) {columns.add(DELETE_USER_ID); params.add(PARAM_DELETE_USER_ID.prefix(InsertStatementProvider.PARAM_PREFIX));}
        if (bookInventoryPo.getRecordVersion() != null) {columns.add(RECORD_VERSION); params.add(PARAM_RECORD_VERSION.prefix(InsertStatementProvider.PARAM_PREFIX));}
        return insertInto(BOOK_INVENTORY, columns.toArray(new DefaultAliasedNamedColumn[0])).values(params.toArray(new MybatisParam[0])).build();
    }

    public InsertStatementProvider<BookInventoryPo> insertSelective(BookInventoryPo bookInventoryPo) {
        String insertSql = sqlBuilder.build(buildInsertSelective(bookInventoryPo), DUMMY_INSTANCE);
        return InsertStatementProviderImpl.builder(insertSql, bookInventoryPo).build();
    }

    protected Insert buildInsertMultiple(List<BookInventoryPo> bookInventoryPos) {
        MybatisParam[][] paramses = new MybatisParam[bookInventoryPos.size()][11];
        for (int i = 0; i < bookInventoryPos.size(); i++) {
            MybatisParam[] params = new MybatisParam[11];
            params[0] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_BOOK_INVENTORY_ID.getParamName()) , PARAM_BOOK_INVENTORY_ID.getJdbcType()).build();
            params[1] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_BOOK_ID.getParamName()) , PARAM_BOOK_ID.getJdbcType()).build();
            params[2] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_BOOK_QUANTITY.getParamName()) , PARAM_BOOK_QUANTITY.getJdbcType()).build();
            params[3] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_LOCATION_CODE.getParamName()) , PARAM_LOCATION_CODE.getJdbcType()).build();
            params[4] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_CREATE_DATE_TIME.getParamName()) , PARAM_CREATE_DATE_TIME.getJdbcType()).build();
            params[5] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_UPDATE_DATE_TIME.getParamName()) , PARAM_UPDATE_DATE_TIME.getJdbcType()).build();
            params[6] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_DELETE_DATE_TIME.getParamName()) , PARAM_DELETE_DATE_TIME.getJdbcType()).build();
            params[7] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_CREATE_USER_ID.getParamName()) , PARAM_CREATE_USER_ID.getJdbcType()).build();
            params[8] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_UPDATE_USER_ID.getParamName()) , PARAM_UPDATE_USER_ID.getJdbcType()).build();
            params[9] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_DELETE_USER_ID.getParamName()) , PARAM_DELETE_USER_ID.getJdbcType()).build();
            params[10] = MybatisParam.builder(String.format("%s[%d].%s", MultiRowsInsertStatementProvider.PARAM_PREFIX, i, PARAM_RECORD_VERSION.getParamName()) , PARAM_RECORD_VERSION.getJdbcType()).build();
            paramses[i] = params;
        }
        return insertInto(BOOK_INVENTORY, COLUMNS).valueses(paramses).build();
    }

    public MultiRowsInsertStatementProvider<BookInventoryPo> insertMultiple(List<BookInventoryPo> bookInventoryPos) {
        String insertSql = sqlBuilder.build(buildInsertMultiple(bookInventoryPos), DUMMY_INSTANCE);
        return MultiRowsInsertStatementProviderImpl.builder(insertSql, bookInventoryPos).build();
    }

    public static Delete buildDeleteByPrimaryKey() {
        return deleteFrom(BOOK_INVENTORY).where(BOOK_INVENTORY_ID.eq(PARAM_BOOK_INVENTORY_ID.prefix(GeneralStatementProvider.PARAM_PREFIX)), and(RECORD_VERSION.eq(PARAM_RECORD_VERSION.prefix(GeneralStatementProvider.PARAM_PREFIX)))).build();
    }

    public GeneralStatementProvider deleteByPrimaryKey(BigInteger bookInventoryId, Long recordVersion) {
        String key = buildKey("deleteByPrimaryKey", BigInteger.class, Long.class);
        String deleteSql = sqlCache.computeIfAbsent(key, k -> sqlBuilder.build(buildDeleteByPrimaryKey(), DUMMY_INSTANCE));
        return GeneralStatementProviderImpl.builder(deleteSql).addParam("bookInventoryId", argNotNull(bookInventoryId, "bookInventoryId")).addParam("recordVersion", argNotNull(recordVersion, "recordVersion")).build();
    }

    protected Update buildUpdateByPrimaryKey(BookInventoryUpdate bookInventoryUpdate) {
        return update(BOOK_INVENTORY)
            .set(c -> consumeIfTrue(bookInventoryUpdate::isBookInventoryIdUpdated, () -> c.set(BOOK_INVENTORY_ID, PARAM_BOOK_INVENTORY_ID.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isBookIdUpdated, () -> c.set(BOOK_ID, PARAM_BOOK_ID.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isBookQuantityUpdated, () -> c.set(BOOK_QUANTITY, PARAM_BOOK_QUANTITY.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isLocationCodeUpdated, () -> c.set(LOCATION_CODE, PARAM_LOCATION_CODE.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isCreateDateTimeUpdated, () -> c.set(CREATE_DATE_TIME, PARAM_CREATE_DATE_TIME.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isUpdateDateTimeUpdated, () -> c.set(UPDATE_DATE_TIME, PARAM_UPDATE_DATE_TIME.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isDeleteDateTimeUpdated, () -> c.set(DELETE_DATE_TIME, PARAM_DELETE_DATE_TIME.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isCreateUserIdUpdated, () -> c.set(CREATE_USER_ID, PARAM_CREATE_USER_ID.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isUpdateUserIdUpdated, () -> c.set(UPDATE_USER_ID, PARAM_UPDATE_USER_ID.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isDeleteUserIdUpdated, () -> c.set(DELETE_USER_ID, PARAM_DELETE_USER_ID.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .set(c -> consumeIfTrue(bookInventoryUpdate::isRecordVersionUpdated, () -> c.set(RECORD_VERSION, PARAM_RECORD_VERSION.prefix(UpdateStatementProvider.UPDATE_PARAM_PREFIX))))
            .where(BOOK_INVENTORY_ID.eq(PARAM_BOOK_INVENTORY_ID.prefix(UpdateStatementProvider.UPDATE_CONDITION_PREFIX)), and(RECORD_VERSION.eq(PARAM_RECORD_VERSION.prefix(UpdateStatementProvider.UPDATE_CONDITION_PREFIX))))
            .build();
    }

    public UpdateStatementProvider updateByPrimaryKey(BookInventoryUpdate bookInventoryUpdate, BigInteger bookInventoryId, Long recordVersion) {
        String updateSql = sqlBuilder.build(buildUpdateByPrimaryKey(bookInventoryUpdate), DUMMY_INSTANCE);
        return UpdateStatementProviderImpl.builder(updateSql, bookInventoryUpdate).addParam("bookInventoryId", argNotNull(bookInventoryId, "bookInventoryId")).addParam("recordVersion", argNotNull(recordVersion, "recordVersion")).build();
    }

    protected Select buildSelectByPrimaryKey() {
        return select(COLUMNS).from(BOOK_INVENTORY).where(BOOK_INVENTORY_ID.eq(PARAM_BOOK_INVENTORY_ID.prefix(GeneralStatementProvider.PARAM_PREFIX))).build();
    }

    public GeneralStatementProvider selectByPrimaryKey(BigInteger bookInventoryId) {
        String key = buildKey("selectByPrimaryKey", BigInteger.class);
        String selectSql = sqlCache.computeIfAbsent(key, k -> sqlBuilder.build(buildSelectByPrimaryKey(), DUMMY_INSTANCE));
        return GeneralStatementProviderImpl.builder(selectSql).addParam("bookInventoryId", argNotNull(bookInventoryId, "bookInventoryId")).build();
    }

    protected Select buildCuntByPrimaryKey() {
        return selectCount().from(BOOK_INVENTORY).where(BOOK_INVENTORY_ID.eq(PARAM_BOOK_INVENTORY_ID.prefix(GeneralStatementProvider.PARAM_PREFIX))).build();
    }

    public GeneralStatementProvider countByPrimaryKey(BigInteger bookInventoryId) {
        String key = buildKey("countByPrimaryKey", BigInteger.class);
        String selectSql = sqlCache.computeIfAbsent(key, k -> sqlBuilder.build(buildCuntByPrimaryKey(), DUMMY_INSTANCE));
        return GeneralStatementProviderImpl.builder(selectSql).addParam("bookInventoryId", argNotNull(bookInventoryId, "bookInventoryId")).build();
    }

    public static Select buildSelectOrCountDynamic(QueryWrapper queryWrapper) {
        return select(queryWrapper.isCount(), queryWrapper.isDistinct(), ObjectHelper.isEmpty(queryWrapper.getColumns()) ? COLUMNS : queryWrapper.getColumns())
            .from(BOOK_INVENTORY)
            .where(queryWrapper.getCondition())
            .orderBy(ObjectHelper.isNotEmpty(queryWrapper.getOrderColumns()), queryWrapper.getOrderColumns())
            .limit(queryWrapper.getLimit() != null && !queryWrapper.isCount(), queryWrapper.getLimit())
            .offset(queryWrapper.getOffset() != null && !queryWrapper.isCount(), queryWrapper.getOffset())
            .forUpdate(queryWrapper.isForUpdate() && !queryWrapper.isCount())
            .build();
    }

    public GeneralStatementProvider selectOrCountDynamic(QueryWrapper queryWrapper) {
        return generalSelect(buildSelectOrCountDynamic(queryWrapper));
    }

    public static Delete buildDeleteDynamic(Condition condition) {
        return deleteFrom(BOOK_INVENTORY).where(condition).build();
    }

    public GeneralStatementProvider deleteDynamic(Condition condition) {
        return generalDelete(buildDeleteDynamic(condition));
    }

    public static Update buildUpdateDynamic(BookInventoryColumnUpdate bookInventoryColumnUpdate, Condition condition) {
        return update(BOOK_INVENTORY)
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isBookInventoryIdUpdated, () -> c.set(BOOK_INVENTORY_ID, bookInventoryColumnUpdate.getBookInventoryId())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isBookIdUpdated, () -> c.set(BOOK_ID, bookInventoryColumnUpdate.getBookId())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isBookQuantityUpdated, () -> c.set(BOOK_QUANTITY, bookInventoryColumnUpdate.getBookQuantity())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isLocationCodeUpdated, () -> c.set(LOCATION_CODE, bookInventoryColumnUpdate.getLocationCode())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isCreateDateTimeUpdated, () -> c.set(CREATE_DATE_TIME, bookInventoryColumnUpdate.getCreateDateTime())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isUpdateDateTimeUpdated, () -> c.set(UPDATE_DATE_TIME, bookInventoryColumnUpdate.getUpdateDateTime())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isDeleteDateTimeUpdated, () -> c.set(DELETE_DATE_TIME, bookInventoryColumnUpdate.getDeleteDateTime())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isCreateUserIdUpdated, () -> c.set(CREATE_USER_ID, bookInventoryColumnUpdate.getCreateUserId())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isUpdateUserIdUpdated, () -> c.set(UPDATE_USER_ID, bookInventoryColumnUpdate.getUpdateUserId())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isDeleteUserIdUpdated, () -> c.set(DELETE_USER_ID, bookInventoryColumnUpdate.getDeleteUserId())))
            .set(c -> consumeIfTrue(bookInventoryColumnUpdate::isRecordVersionUpdated, () -> c.set(RECORD_VERSION, bookInventoryColumnUpdate.getRecordVersion())))
            .where(condition)
            .build();
    }

    public GeneralStatementProvider updateDynamic(BookInventoryColumnUpdate bookInventoryColumnUpdate, Condition condition) {
        return generalUpdate(buildUpdateDynamic(bookInventoryColumnUpdate, condition));
    }
}
